﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Common.Framework
{
    /// <summary>
    /// 国际化的通用框架, 使用示例(webapi):
    /// <example>
    /// <code>
    /// var builder = WebApplication.CreateBuilder(args);
    /// //先注册国际化kv
    /// I18N.RegisterLanguage("zh", new Dictionary&lt;string, string> { { "name", "杰克" } });
    /// I18N.RegisterLanguage("en", new Dictionary&lt;string, string> { { "name", "jack" } });
    /// 
    /// var app = builder.Build();
    /// //使用中间件拦截请求,设置当前使用的 language
    /// app.Use(async (ctx, next) =>
    /// {
    ///     if (string.Equals(ctx.Request.Query["lang"], "zh", StringComparison.OrdinalIgnoreCase))
    ///     {
    ///         I18N.SetCurrentLanguage("zh");
    ///     }
    ///     else
    ///     {
    ///         I18N.SetCurrentLanguage("en");
    ///     }
    ///     await next();
    /// });
    /// app.MapGet("/", () =>
    /// {
    ///     return I18N.GetString("name");
    /// });
    /// app.Run();
    ///
    /// //"http://localhost:5270/?lang=zh": 后台服务返回: "杰克"
    /// //"http://localhost:5270/?lang=en": 后台服务返回: "jack"
    /// </code>
    /// </example>
    /// </summary>
    public static class I18N
    {
        private const string I18N_LANGUAGE = "I18N_LANGUAGE";

        #region Register...
        private static readonly Dictionary<string, Dictionary<string, string>> languageStrings = new Dictionary<string, Dictionary<string, string>>();
        private static Dictionary<string, string> defaultLanguageStrings = new Dictionary<string, string>();
        /// <summary>
        /// 注册语言的kv映射(可以指定是否作为默认的语言):
        /// <code>
        /// I18N.RegisterLanguage("zh", new Dictionary&lt;string, string> { { "name", "杰克" } });
        /// </code>
        /// </summary>
        public static void RegisterLanguage(string language, Dictionary<string, string> kvs, bool asDefault = false)
        {
            Ensure.NotNull(kvs, nameof(kvs));
            languageStrings[language] = kvs;
            if (asDefault) defaultLanguageStrings = kvs;
        }
        private static Func<string, string> fallBackFunc = null;
        /// <summary>
        /// 注册当找不到语言的kv映射时的处理逻辑, 如: 没有注册语言 "zh-CN" 或 没有在语言 "zh-CN" 中找到 "name" 的映射, 使用示例:
        /// <code>
        /// I18N.RegisterFallBack(key => $"cannot find {key}.");
        /// </code>
        /// </summary>
        public static void RegisterFallBack(Func<string, string> func)
        {
            fallBackFunc = func;
        }
        private static Func<string, Task<string>> fallBackFuncAsync = null;
        /// <summary>
        /// 注册当找不到语言的kv映射时的处理逻辑, 如: 没有注册语言 "zh-CN" 或 没有在语言 "zh-CN" 中找到 "name" 的映射, 使用示例:
        /// <code>
        /// I18N.RegisterFallBack(key => $"cannot find {key}.");
        /// </code>
        /// </summary>
        public static void RegisterFallBack(Func<string, Task<string>> func)
        {
            fallBackFuncAsync = func;
        }
        #endregion

        #region CurrentLanguage
        /// <summary>
        /// 设置当前运行环境的语言, 也可以使用 <seealso cref="RunInLanguage(string, Action)"/>
        /// </summary>
        public static void SetCurrentLanguage(string language)
        {
            RunningContext.Current.SetValue(I18N_LANGUAGE, language);
        }

        /// <summary>
        /// 获取当前运行环境设置的语言, 如果还没有设置则抛出异常
        /// </summary>
        public static string GetCurrentLanguage()
        {
            return RunningContext.Current.GetValue<string>(I18N_LANGUAGE);
        }

        /// <summary>
        /// 判断当前环境是否设置了语言
        /// </summary>
        public static bool HasSetLanguage()
        {
            return RunningContext.Current.ContainsKey(I18N_LANGUAGE);
        }

        /// <summary>
        /// 尝试获取当前环境设置的语言
        /// </summary>
        public static bool TryGetLanguage(out string lang)
        {
            return RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out lang);
        }
        #endregion    

        #region RunInLanguag
        /// <summary>
        /// 使 func 下的代码都运行在 language 环境中, 如:
        /// <code>
        /// I18N.RunInLanguage("zh-CN", () =>
        /// {
        ///     //some code
        /// });
        /// </code>
        /// </summary>
        public static async Task RunInLanguageAsync(string language, Func<Task> func)
        {
            await RunningContext.RunInContextAsync(async () =>
            {
                SetCurrentLanguage(language);
                await func();
            });
        }

        /// <summary>
        /// 使 func 下的代码都运行在 language 环境中, 如:
        /// <code>
        /// I18N.RunInLanguage("zh-CN", () =>
        /// {
        ///     //some code
        /// });
        /// </code>
        /// </summary>
        public static void RunInLanguage(string language, Action func)
        {
            RunningContext.RunInContext(() =>
            {
                SetCurrentLanguage(language);
                func();
            });
        }
        #endregion

        #region GetString
        /// <summary>
        /// 获取当前语言下的 key 的映射
        /// </summary>
        public static string GetString(string key)
        {
            if (RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out var language))
            {
                if (languageStrings.TryGetValue(language, out var languageDic))
                {
                    if (languageDic.TryGetValue(key, out var value)) return value;
                }
            }
            if (defaultLanguageStrings != null && defaultLanguageStrings.TryGetValue(key, out var value2)) return value2;
            if (fallBackFunc != null) return fallBackFunc(key);
            if (fallBackFuncAsync != null) return fallBackFuncAsync(key).Result;
            return null;
        }

        /// <summary>
        /// 获取当前语言下的 key 的映射
        /// </summary>
        public static async Task<string> GetStringAsync(string key)
        {
            if (RunningContext.Current.TryGetValue<string>(I18N_LANGUAGE, out var language))
            {
                if (languageStrings.TryGetValue(language, out var languageDic))
                {
                    if (languageDic.TryGetValue(key, out var value)) return value;
                }
            }
            if (defaultLanguageStrings != null && defaultLanguageStrings.TryGetValue(key, out var value2)) return value2;
            if (fallBackFuncAsync != null) return await fallBackFuncAsync(key);
            if (fallBackFunc != null) return fallBackFunc(key);
            return null;
        }
        #endregion
    }
}
